﻿using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Security.Cryptography;
using System.Text;

namespace Vkontakte.Api
{
	public static class Utils
	{
		private static MD5Cng _md5 = new MD5Cng();

		private const string LoginPatern = "http://vkontakte.ru/login.php?app={0}&layout=popup&type=browser&settings={1}";
		private const string ApiPattern = "http://api.vkontakte.ru/api.php?";

		public static string GetLoginUri(int appID, ApplicationAccess access)
		{
			return string.Format(LoginPatern,
								 appID,
								 (int)access);
		}

		#region MakeQuerry
		public static string MakeQuerry(long userID, string[,] namesAndParams, string sid, string secret)
		{
			List<string> nameAndParam = new List<string>(namesAndParams.GetLength(0));
			for (int i = 0; i < namesAndParams.GetLength(0); i++)
			{
				nameAndParam.Add(string.Format("{0}={1}", namesAndParams[i, 0], namesAndParams[i, 1]));
			}
			return MakeQuerryInternal(userID, nameAndParam, sid, secret);
		}

		public static string MakeQuerry(long userID, IList<string> names, IList<string> parameters, string sid, string secret)
		{
			Contract.Requires(names != null);
			Contract.Requires(parameters != null);
			Contract.Requires(sid != null);
			Contract.Requires(names.Count == parameters.Count);

			List<string> nameAndParam = new List<string>(names.Count);
			for (int i = 0; i < names.Count; i++)
			{
				nameAndParam.Add(string.Format("{0}={1}", names[i], parameters[i]));
			}
			return MakeQuerryInternal(userID, nameAndParam, sid, secret);
		}

		public static string MakeQuerry(long userID, string[] names, string[] parameters, string sid, string secret)
		{
			Contract.Requires(names != null);
			Contract.Requires(parameters != null);
			Contract.Requires(sid != null);
			Contract.Requires(names.Length == parameters.Length);

			List<string> nameAndParam = new List<string>(names.Length);
			for (int i = 0; i < names.Length; i++)
			{
				nameAndParam.Add(string.Format("{0}={1}", names[i], parameters[i]));
			}
			return MakeQuerryInternal(userID, nameAndParam, sid, secret);
		}

		private static string MakeQuerryInternal(long userID, List<string> namesAndParams, string sid, string secret)
		{
			namesAndParams.Sort((a, b) => (a.CompareTo(b)));

			string ret = namesAndParams[0];
			string forHash = userID + namesAndParams[0];
			for (int i = 1; i < namesAndParams.Count; i++)
			{
				ret += "&" + namesAndParams[i].Replace("&", "%26");
				forHash += namesAndParams[i];
			}
			ret = ret.Replace("+", "%2B").Replace("#", "%23");
			byte[] hash = _md5.ComputeHash(System.Text.Encoding.UTF8.GetBytes(forHash + secret));
			string sig = hash.ToHexString();
			return string.Format("{0}{1}&sid={2}&sig={3}", ApiPattern, ret, sid, sig);
		}
		#endregion

		public static string GetLongPollUri(LongPollServerData data)
		{
			return GetLongPollUri( data.Server, data.Key, data.Timestamp );
		}

		public static string GetLongPollUri(string server,string key,long ts)
		{
			return string.Format( "http://{0}?act=a_check&key={1}&ts={2}&wait=25", server, key, ts );
		}
		
		public static string ToHexString(this byte[] value)
		{
			StringBuilder hex = new StringBuilder(value.Length * 2);
			foreach (byte b in value)
				hex.AppendFormat("{0:x2}", b);
			return hex.ToString();
		}

		public static string ToStr(this NameCase value)
		{
			switch (value)
			{
				case NameCase.Nom:
					return "nom";
				case NameCase.Gen:
					return "gen";
				case NameCase.Dat:
					return "dat";
				case NameCase.Acc:
					return "acc";
				case NameCase.Ins:
					return "ins";
				case NameCase.Abl:
					return "abl";
			}
			return "";
		}

		private static readonly string[] _personProperties = new string[]
		                                                     	{
		                                                     		"uid",
																	"first_name",
																	"last_name",
																	"nickname",
																	"sex",
																	"bdate",
																	"city",
																	"country",
																	"timezone",
																	"photo",
																	"photo_medium",
																	"photo_big",
																	"photo_rec",
																	"online",
																	"lists",
																	"domain",
																	"has_mobile",
																	"rate",
																	"contacts",
																	"education"
		                                                     	};

		public static string ToStr(this PersonProperties value)
		{
			string ret = null;
			int intValue = (int)value;
			for (int i = 0; i < _personProperties.Length; i++)
			{
				if ((intValue & (1 << i)) != 0)
				{
					ret = (ret == null) ? _personProperties[i] : ret + "," + _personProperties[i];
				}
			}
			return ret;
		}


		private static readonly DateTime _linuxDateTime = new DateTime(1970, 1, 1, 0, 0, 0);

		public static double ToUnixTimeStamp(this DateTime value)
		{
			var q = value - _linuxDateTime;
			return q.TotalSeconds;
		}

		public static DateTime FromUnixTimeStampToDateTime(long timestamp)
		{
			DateTime ret = _linuxDateTime;

			return ret.AddSeconds(timestamp);
		}

		/// <summary>
		/// Исправляет некоторые 'фичи' серриализации контакта
		/// </summary>
		/// <param name="value"></param>
		/// <returns></returns>
		public static string FixXmlString(this string value)
		{
			int ptr = 0;
			int end = 0;

			while ((ptr = value.IndexOf('&', 0)) != -1)
			{
				end = value.IndexOf(';', ptr);

				value = value.Substring(0, ptr)
					+ GetEntity(value.Substring(ptr + 1, end - ptr - 1))
					+ value.Substring(end + 1);
			}

			return value;
		}

		private static string GetEntity(string value)
		{
			switch (value)
			{
				case "quot":
					return "\"";
				case "amp":
					return "&";
				case "#39":
					return "'";
				default:
					return "";
			}
		}

		public static string FixJsonString(this string value)
		{
			return value != null ? value.Replace("&amp;", "&") : null;
		}

	}
}